package com.zzl.finance.data.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzl.finance.data.constant.ProductStatusEnum;
import com.zzl.finance.data.constant.StringConstant;
import com.zzl.finance.data.dao.FinancialProductDao;
import com.zzl.finance.data.dto.*;
import com.zzl.finance.data.entity.FinanceInstitutyEntity;
import com.zzl.finance.data.entity.FinancialProductEntity;
import com.zzl.finance.data.entity.InstitutyDataEntity;
import com.zzl.finance.data.feign.BondsTicketFeign;
import com.zzl.finance.data.feign.TicketFeign;
import com.zzl.finance.data.service.EnterpriseFinancialProductService;
import com.zzl.finance.data.service.FinanceInstitutyService;
import com.zzl.finance.data.service.FinancialProductService;
import com.zzl.finance.data.service.InstitutyDataService;
import com.zzl.general.common.constant.Constant;
import com.zzl.general.common.exception.SysException;
import com.zzl.general.common.serurity.entity.User;
import com.zzl.general.common.utils.*;
import com.zzl.general.common.utils.scriptutils.exception.JavaScriptUtilsException;
import com.zzl.general.common.utils.scriptutils.script.JavaScriptBean;
import com.zzl.general.common.utils.scriptutils.script.JavaScriptExcuteUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.zzl.finance.data.constant.StringConstant.FINANCIAL_PRODUCT_SCRIPT_CALCULATE_METHOD_NAME;
import static com.zzl.finance.data.constant.StringConstant.GET_DETAIL;

/**
 * 金融产品
 *
 * @author wztsl
 * @date 2021-12-22 19:40:27
 */
@Slf4j
@Service("financialProductService")
public class FinancialProductServiceImpl extends ServiceImpl<FinancialProductDao, FinancialProductEntity> implements FinancialProductService {

    @Autowired
    @SuppressWarnings("all")
    private TicketFeign ticketFeign;
    @Autowired
    @SuppressWarnings("all")
    private FinanceInstitutyService financeInstitutyService;
    @Autowired
    private BondsTicketFeign bondsTicketFeign;
    @Autowired
    private EnterpriseFinancialProductService enterpriseFinancialProductService;
    @Autowired
    private InstitutyDataService institutyDataService;


    /**
     * 分页查询
     *
     * @param params 查询参数
     * @return 分页数据
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        //排序字段：create_time
        String pageField = (String) params.get(Constant.PAGE);
        //排序方式 order:  desc asc
        String order = (String) params.get(Constant.ORDER);
        IPage<FinancialProductListDto> page = new Query<FinancialProductListDto>()
                .getPage(params, StringUtils.isBlank(pageField)
                        ? StringConstant.CREATE_TIME_FILED : pageField, Constant.ASC.equals(order));
        IPage<FinancialProductListDto> financeProductPage = this.baseMapper.queryProductPage(page, params);
        return new PageUtils(financeProductPage);
    }

    /**
     * 新增
     *
     * @param financialProductAddDto 新增实体
     * @param user                   当前登录用户
     * @throws SysException 业务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveFinancialProduct(FinancialProductAddDto financialProductAddDto, User user) throws SysException {
        //一个金融机构，只能有一个金融产品
//        QueryWrapper<FinancialProductEntity> queryWrapper = new QueryWrapper<>();
//        queryWrapper.lambda().eq(FinancialProductEntity::getInstitutyId, financialProductAddDto.getFinanceInstitutyId());
//        List<FinancialProductEntity> productEntities = this.list(queryWrapper);
//        if (!CollectionUtils.isEmpty(productEntities)) {
//            throw new SysException("当前金融机构已绑定金融产品，不能重复创建");
//        }
//
//
//        FinancialProductEntity financialProductEntity = new FinancialProductEntity();
//        BeanUtils.copyProperties(financialProductAddDto, financialProductEntity);
//        financialProductEntity.setCreatorId(user.getId());
//        if (!this.save(financialProductEntity)) {
//            throw new SysException("A00001", "保存失败！");
//        }
//        //处理企业服务费
//        List<EnterpriseFinancialProductAddDto> list = financialProductAddDto.getList();
//        if (!CollectionUtils.isEmpty(list)) {
//            enterpriseFinancialProductService.saveEnterpriseFinancialProduct(list, financialProductEntity.getId());
//        }
    }

    /**
     * 修改
     *
     * @param financialProductUpdateDto 修改实体
     * @param user                      当前登录用户
     * @throws SysException 业务异常
     */
    @Override
    public void updateFinancialProductById(FinancialProductUpdateDto financialProductUpdateDto, User user) throws SysException {
        FinancialProductEntity financialProductEntity = new FinancialProductEntity();
        BeanUtils.copyProperties(financialProductUpdateDto, financialProductEntity);
        financialProductEntity.setUpdaterId(user.getId());
        if (!this.updateById(financialProductEntity)) {
            throw new SysException("A00002", "修改失败！");
        }
        //处理企业费率
        List<EnterpriseFinancialProductUpdateDto> list = financialProductUpdateDto.getList();
        if (!CollectionUtils.isEmpty(list)) {
            enterpriseFinancialProductService.saveOrUpdateProduct(list, financialProductUpdateDto.getId());
        }
    }

    /**
     * 通过金融机构ids查询金融机构及关联金融产品
     *
     * @param ids 金融机构id集合
     * @return 金融机构及产品
     */
    @Override
    public List<FinanceInstitutyIncludeProductDto> listAllFinanceInstitutyAndProduct(List<Integer> ids) {
        QueryWrapper<FinanceInstitutyEntity> institutyQueryWrapper = new QueryWrapper<>();
        if (!CollectionUtils.isEmpty(ids)) {
            institutyQueryWrapper.in("id", ids);
        }
        institutyQueryWrapper.orderByAsc("sort");
        List<FinanceInstitutyEntity> institutyList = financeInstitutyService.list(institutyQueryWrapper);
        if (CollectionUtils.isEmpty(institutyList)) {
            return new ArrayList<>();
        }
        List<FinanceInstitutyIncludeProductDto> result = QmBeanUtils.copyListProperties(institutyList, FinanceInstitutyIncludeProductDto::new);
        QueryWrapper<FinancialProductEntity> queryWrapper = new QueryWrapper<>();
        if (!CollectionUtils.isEmpty(ids)) {
            queryWrapper.in("finance_instituty_id", ids);
        }
        queryWrapper.orderByAsc("sort");
        List<FinancialProductEntity> productList = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(productList)) {
            return result;
        }
        Map<String, List<FinancialProductEntity>> collect = productList.stream().collect(Collectors.groupingBy(FinancialProductEntity::getInstitutyId));
        for (FinanceInstitutyIncludeProductDto dto : result) {
            List<FinancialProductEntity> financialProductList = collect.get(dto.getId());
            if (CollectionUtils.isEmpty(financialProductList)) {
                dto.setProductList(new ArrayList<>());
            } else {
                dto.setProductList(financialProductList);
            }
        }
        return result;
    }

    /**
     * 通过金融机构及关联金融产品计算票据融资情况
     *
     * @param ticketMap 票据对象map
     * @return 金融机构、金融产品及融资情况
     * @throws SysException 业务异常
     */
    @Override
    public List<FinanceInstitutyAndProductDto> calcInstitutyAndProduct(Map<String, Object> ticketMap) throws SysException {
        List<FinanceInstitutyIncludeProductDto> institutyIncludeProductList = this.listAllFinanceInstitutyAndProduct(new ArrayList<>());
        if (CollectionUtils.isEmpty(institutyIncludeProductList)) {
            return new ArrayList<>();
        }
        List<FinanceInstitutyAndProductDto> result = new ArrayList<>();
        for (FinanceInstitutyIncludeProductDto institutyIncludeProductDto : institutyIncludeProductList) {
            FinanceInstitutyAndProductDto resultDto = new FinanceInstitutyAndProductDto();
            BeanUtils.copyProperties(institutyIncludeProductDto, resultDto);
            List<FinancialProductCalcDto> productCalcList = new ArrayList<>();
            resultDto.setProductCalcList(productCalcList);
            result.add(resultDto);
            List<FinancialProductEntity> productList = institutyIncludeProductDto.getProductList();
            if (CollectionUtils.isEmpty(productList)) {
                continue;
            }
            for (FinancialProductEntity entity : productList) {
                FinancialProductCalcDto financialProductCalcDto = new FinancialProductCalcDto();
                financialProductCalcDto.setId(entity.getId());
                financialProductCalcDto.setProductName(entity.getProductName());
                if (CollectionUtils.isEmpty(ticketMap)) {
                    this.calcInstitutyAndProductTicketMapIsNull(financialProductCalcDto, entity);
                } else {
                    this.calcInstitutyAndProductTicketMapNotNull(ticketMap, entity, financialProductCalcDto);
                }
                productCalcList.add(financialProductCalcDto);
            }
        }
        return result;
    }

    /**
     * 通过金融产品id计算票据融资情况
     *
     * @param ticketMap 票据对象map
     * @param productId 金融产品id
     * @return 金融机构、金融产品及融资情况
     * @throws SysException 业务异常
     */
    @Override
    public FinancialProductCalculateTicketDto calcProduct(Map<String, Object> ticketMap, Integer productId) throws SysException {
        FinancialProductEntity productEntity = this.getById(productId);
        return this.calcInstitutyAndProductTicketMapNotNull(ticketMap, productEntity, new FinancialProductCalcDto());
    }

    /**
     * 获取金融产品列表数据
     *
     * @return 金融产品集合
     */
    @Override
    public List<FinancialProductDto> getFinancialProductList(User user, String enterpriseId) {
        List<FinancialProductDto> financialProductAvailable = new ArrayList<>();
        //所有金融机构以及金融产品
        List<FinancialProductDto> financialProduct = this.getBaseMapper().getFinancialProduct();
        if (!CollectionUtils.isEmpty(financialProduct)) { //todo gull
//            QueryWrapper<EnterpriseFinancialProductEntity> queryWrapper = new QueryWrapper<>();
//            queryWrapper.lambda().eq(EnterpriseFinancialProductEntity::getEnterpriseId, user.getEnterpriseId());
//            List<EnterpriseFinancialProductEntity> list = enterpriseFinancialProductService.list(queryWrapper);
//            Map<Integer, EnterpriseFinancialProductEntity> enterpriseFinancialProductEntityMap = null;
//            if (!CollectionUtils.isEmpty(list)) {
//                enterpriseFinancialProductEntityMap =
//                        list.stream().collect(Collectors.toMap(EnterpriseFinancialProductEntity::getFinancialProductId, a -> a, (k1, k2) -> k2));
//            }
//            List<SupplierCreditEntity> financeList = bondsTicketFeign.getFinanceList(enterpriseId == null ? 0 : enterpriseId);
//            Map<Integer, FinancialProductDto> collect = financialProduct.stream().collect(Collectors.toMap(FinancialProductDto::getFinanceInstitutyId, a -> a, (k1, k2) -> k2));
//            if (!CollectionUtils.isEmpty(financeList)) {
//                Map<Integer, EnterpriseFinancialProductEntity> finalEnterpriseFinancialProductEntityMap = enterpriseFinancialProductEntityMap;
//                financeList.forEach(x -> {
//                    if (x.getCreditAvailableAmount() > 0) {
//                        FinancialProductDto financialProductDto = collect.get(x.getFinanceInstitutyId());
//                        financialProductDto.setCreditNo(x.getCreditNo());
//                        financialProductDto.setFinancingAmount(x.getCreditAvailableAmount());
//                        financialProductDto.setSupplierCreditId(x.getId());
//                        //查询公司的服务费率，没有则默认千2
//                        if (null != finalEnterpriseFinancialProductEntityMap) {
//                            EnterpriseFinancialProductEntity enterpriseFinancialProductEntity = finalEnterpriseFinancialProductEntityMap.get(x.getFinanceInstitutyId());
//                            financialProductDto.setPlatServiceRate(enterpriseFinancialProductEntity == null ?
//                                    StringConstant.PLAT_SERVICE_RATE : enterpriseFinancialProductEntity.getPlatServiceRate());
//                        }
//
//                        financialProductAvailable.add(financialProductDto);
//                    }
//                });
//            }
        }
        return financialProductAvailable;
    }

    /**
     * 根据金融产品id查询产品信息
     *
     * @param productId 产品id
     * @param user      用户
     * @return 产品信息
     */
    @Override
    public FinancialProductEntity financialProductById(String productId, User user) throws SysException {
        FinancialProductEntity productEntity = this.getById(productId);
        if (ProductStatusEnum.PRODUCT_STATUS_INVALID.getCode().equals(productEntity.getProductStatus())) {
            throw new SysException("当前产品已失效，不可以进行融资");
        }
        //todo gull
//        QueryWrapper<EnterpriseFinancialProductEntity> queryWrapper = new QueryWrapper<>();
//        queryWrapper.lambda().eq(EnterpriseFinancialProductEntity::getEnterpriseId, user.getEnterpriseId())
//                .eq(EnterpriseFinancialProductEntity::getFinancialProductId, productEntity.getId());
//        List<EnterpriseFinancialProductEntity> list = enterpriseFinancialProductService.list(queryWrapper);
//        if (!CollectionUtils.isEmpty(list)) {
//            EnterpriseFinancialProductEntity enterpriseFinancialProductEntity = list.get(0);
//            productEntity.setPlatServiceRate(enterpriseFinancialProductEntity.getPlatServiceRate());
//        }
        return productEntity;

    }

    /**
     * 根据资金方id查询产品信息列表
     *
     * @param financeId 资金方id
     * @param user      用户
     * @return 产品信息列表
     */
    @Override
    public List<FinancialProductInfoDto> getProductByfinanceId(String financeId, User user) {
        QueryWrapper<FinancialProductEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(FinancialProductEntity::getInstitutyId, financeId);
        List<FinancialProductEntity> productEntityList = this.list(queryWrapper);
        List<FinancialProductInfoDto> result = new ArrayList<>(productEntityList.size());
        if (!CollectionUtils.isEmpty(productEntityList)) {
            List<String> idList = productEntityList.stream().map(FinancialProductEntity::getId).collect(Collectors.toList());
            QueryWrapper<InstitutyDataEntity> dataQueryWrapper = new QueryWrapper<>();
            dataQueryWrapper.lambda().in(InstitutyDataEntity::getFinanceProductId, idList);
            List<InstitutyDataEntity> list = institutyDataService.list(dataQueryWrapper);
            Map<String, List<InstitutyDataEntity>> collect = list.stream()
                    .collect(Collectors.groupingBy(InstitutyDataEntity::getFinanceProductId));
            productEntityList.forEach(x -> {
                FinancialProductInfoDto financialProductInfoDto = new FinancialProductInfoDto();
                BeanUtils.copyProperties(x, financialProductInfoDto);
                financialProductInfoDto.setDataInfoDtoList(collect.get(x.getId()));
                result.add(financialProductInfoDto);
            });
        }
        return result;

    }

    /**
     * 保存子产品配置
     *
     * @param productAddDtoList 融资产品
     * @param institutyId       金融机构id
     * @param user              用户
     */
    @Override
    public void saveBatchFinancialProduct(List<FinancialProductAddDto> productAddDtoList, String institutyId, User user) throws SysException {
        if (!CollectionUtils.isEmpty(productAddDtoList)) {
            List<FinancialProductEntity> productList = new ArrayList<>(productAddDtoList.size());
            List<InstitutyDataEntity> dataList = new ArrayList<>(productAddDtoList.size());
            productAddDtoList.forEach(x -> {
                FinancialProductEntity financialProduct = new FinancialProductEntity();
                BeanUtils.copyProperties(x, financialProduct);
                financialProduct.setCreatorId(user.getId());
                financialProduct.setUpdaterId(user.getId());
                financialProduct.setId(UuidUtils.UUID());
                List<InstitutyDataInfoDto> dataInfoDtoList = x.getDataInfoDtoList();
                dataInfoDtoList.forEach(y -> {
                    InstitutyDataEntity institutyData = new InstitutyDataEntity();
                    institutyData.setId(UuidUtils.UUID());
                    institutyData.setFinanceProductId(financialProduct.getId());
                    institutyData.setCreatorId(user.getId());
                    institutyData.setUpdaterId(user.getId());
                    institutyData.setEnterpriseId(user.getEnterpriseId());
                    dataList.add(institutyData);
                });

                productList.add(financialProduct);
            });
            if (!this.saveBatch(productList) || !institutyDataService.saveBatch(dataList)) {
                throw new SysException("子产品配置保存失败");
            }

        }


    }

    /**
     * 通过金融机构及关联金融产品计算票据融资情况
     * 当未传入票据数据或传入票据空属性时
     *
     * @param financialProductCalcDto 金融产品计算dto
     */
    private void calcInstitutyAndProductTicketMapIsNull(FinancialProductCalcDto financialProductCalcDto, FinancialProductEntity entity) throws SysException {
        String financingCostDetail = "含融资利息：-元（{{financingInterestRate}}利率） " +
                "平台服务费：-元（融资金额{{platformServiceFeeRate}}）";
        try {
            FinancialProductScriptDetailDto detailDto = this.callScriptDetailFunction(entity);
            if (detailDto != null) {
                financingCostDetail = financingCostDetail
                        .replace("{{financingInterestRate}}", String.format("%.4f", detailDto.getFinancingInterestRate() * 100))
                        .replace("{{platformServiceFeeRate}}", String.format("%.4f", detailDto.getPlatformServiceFeeRate() * 100));
                financialProductCalcDto.setFinancingCostDetail(financingCostDetail);
            }
        } catch (JavaScriptUtilsException e) {
            LogUtils.logException(log, e.getCode(), e.getMsg());
            throw new SysException(e.getCode(), e.getMessage());
        }
    }

    /**
     * 通过金融机构及关联金融产品计算票据融资情况
     * 当传入票据数据时
     *
     * @param ticketMap               票据对象map
     * @param entity                  金融产品数据库对象
     * @param financialProductCalcDto 金融产品计算dto
     * @throws SysException 业务异常
     */
    private FinancialProductCalculateTicketDto calcInstitutyAndProductTicketMapNotNull(Map<String, Object> ticketMap,
                                                                                       FinancialProductEntity entity,
                                                                                       FinancialProductCalcDto financialProductCalcDto) throws SysException {
        FinancialProductCalculateTicketDto calcResult;
        try {
            calcResult = this.callScriptCalculateFunction(entity, ticketMap);
        } catch (JavaScriptUtilsException e) {
            LogUtils.logException(log, e.getCode(), e.getMsg());
            throw new SysException(e.getCode(), e.getMessage());
        }
        if (calcResult != null) {
            financialProductCalcDto.setEstimateLoanAmount(calcResult.getEstimatedLoanAmount());
            financialProductCalcDto.setFinancingCost(calcResult.getFinancingCost());
            String financingCostDetail = "含融资利息：{{financingInterest}}元（{{financingInterestRate}}%利率） " +
                    "平台服务费：{{platformServiceFee}}元（融资金额{{platformServiceFeeRate}}%）";
            financingCostDetail = financingCostDetail.replace("{{financingInterest}}", String.format("%.2f", calcResult.getFinancingInterest()))
                    .replace("{{financingInterestRate}}", String.format("%.2f", calcResult.getFinancingInterestRate() * 100))
                    .replace("{{platformServiceFee}}", String.format("%.2f", calcResult.getPlatformServiceFee()))
                    .replace("{{platformServiceFeeRate}}", String.format("%.2f", calcResult.getPlatformServiceFeeRate() * 100));
            financialProductCalcDto.setFinancingCostDetail(financingCostDetail);
            financialProductCalcDto.setFinancingCostRate((calcResult.getFinancingInterestRate() + calcResult.getPlatformServiceFeeRate()) * 100);
        }
        return calcResult;
    }

    /**
     * 执行金融产品脚本，获取金融产品其他信息
     *
     * @param financialProduct 金融产品对象
     * @param ticket           票据对象
     * @return 其他信息集合
     * @throws JavaScriptUtilsException 执行脚本时可能抛出异常
     */
    @Nullable
    private FinancialProductCalculateTicketDto callScriptCalculateFunction(FinancialProductEntity financialProduct, Object ticket)
            throws JavaScriptUtilsException {
        JavaScriptBean javaScriptBean = new JavaScriptBean();
//        javaScriptBean.setScript(financialProduct.getScriptCode());
        javaScriptBean.setMethodName(FINANCIAL_PRODUCT_SCRIPT_CALCULATE_METHOD_NAME);
        javaScriptBean.setParams(new Object[]{JSON.toJSON(ticket)});
        javaScriptBean.setReturnClazz(FinancialProductCalculateTicketDto.class);
        Object result = JavaScriptExcuteUtils.callScripFunction(javaScriptBean);
        if (result == null) {
            return null;
        }
        return (FinancialProductCalculateTicketDto) result;
    }

    /**
     * 执行金融产品脚本，获取金融产品其他信息
     *
     * @param financialProduct 金融产品对象
     * @return 其他信息集合
     * @throws JavaScriptUtilsException 执行脚本时可能抛出异常
     */
    @Nullable
    private FinancialProductScriptDetailDto callScriptDetailFunction(FinancialProductEntity financialProduct)
            throws JavaScriptUtilsException {
        JavaScriptBean javaScriptBean = new JavaScriptBean();
//        javaScriptBean.setScript(financialProduct.getScriptCode());
        javaScriptBean.setMethodName(GET_DETAIL);
        javaScriptBean.setParams(new Object[]{});
        javaScriptBean.setReturnClazz(FinancialProductScriptDetailDto.class);
        Object result = JavaScriptExcuteUtils.callScripFunction(javaScriptBean);
        if (result == null) {
            return null;
        }
        return (FinancialProductScriptDetailDto) result;
    }

}